package msg

import (
	"encoding/binary"
	"errors"

	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/types/dynamicpb"
	"xgame.go_server/comm/log"
)

// 读取消息代号,
// 也就是从 msgData 的前 4 个字节中读取出一个 int32 类型的数值
func ReadMsgCode(msgData []byte) int32 {
	if nil == msgData ||
		len(msgData) < 4 {
		return -1
	}

	msgCode := binary.LittleEndian.Uint32(msgData[msgCodeByteStartIndex:msgCodeByteEndIndex])
	return int32(msgCode)
}

// Decode 根据消息代号对消息数据进行解码
// msgData = 消息数据
// 将返回 protobuf 动态消息对象
func Decode(msgData []byte) (int32, *dynamicpb.Message, error) {
	if nil == msgData ||
		len(msgData) < 4 {
		return -1, nil, errors.New("消息数据为空或长度不够")
	}

	// 获取消息代号
	msgCode := ReadMsgCode(msgData)
	// 根据消息代号获取消息描述
	msgDesc, err := getMsgDescByMsgCode(msgCode)

	if nil != err {
		return -1, nil, err
	}

	// 创建新消息
	newMsgX := dynamicpb.NewMessage(msgDesc)

	// 执行消息解码
	if err := proto.Unmarshal(msgData[msgBodyByteStartIndex:], newMsgX); nil != err {
		return -1, nil, err
	}

	return msgCode, newMsgX, nil
}

// Encode 消息编码
// msgObj = 消息对象
// 将返回编码后的字节数组...
func Encode(pMsgObj protoreflect.ProtoMessage) ([]byte, error) {
	if nil == pMsgObj {
		return nil, errors.New("消息对象为空")
	}

	// 获取消息代号
	msgCode, err := getMsgCodeByMsgName(string(pMsgObj.ProtoReflect().Descriptor().Name()))

	if nil != err {
		log.Error("获取消息代号失败")
		return nil, err
	}

	msgCodeByteArray := make([]byte, 4)
	binary.LittleEndian.PutUint32(msgCodeByteArray, uint32(msgCode))

	msgBodyByteArray, err := proto.Marshal(pMsgObj)

	if nil != err {
		log.Error("消息编码失败, msgCode = %d", msgCode)
		return nil, err
	}

	result := append(msgCodeByteArray, msgBodyByteArray...)

	return result, nil
}
